home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue44 / alfresco / AAHuffmn.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-02-28  |  23.2 KB  |  739 lines

  1. {*********************************************************}
  2. {* AAHuffmn                                              *}
  3. {* Copyright (c) Julian M Bucknall 1999                  *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Huffman compression and decompression                 *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AAHuffmn;
  14.  
  15. interface
  16.  
  17. uses
  18.   SysUtils, Classes;
  19.  
  20. {$IFOPT D+}
  21. {$DEFINE InDebugMode}
  22. {$ENDIF}
  23.  
  24. procedure HuffmanCompress(aInStream, aOutStream : TStream);
  25. procedure HuffmanDecompress(aInStream, aOutStream : TStream);
  26.  
  27. implementation
  28.  
  29. const
  30.   vaByte    = 0;   {value is a byte: 0..255}
  31.   vaWord    = 1;   {value is a word: 255..65535}
  32.   vaLongint = 2;   {value is a longint: all other values}
  33.  
  34. const
  35.   Bit : array [0..7] of byte =  {bit masks}
  36.         ($01, $02, $04, $08, $10, $20, $40, $80);
  37.  
  38. type
  39.   PHuffmanNode = ^THuffmanNode;
  40.   THuffmanNode = packed record
  41.     hnCount    : longint;
  42.     hnLeftInx  : longint;
  43.     hnRightInx : longint;
  44.   end;
  45.  
  46.   PHuffmanTree = ^THuffmanTree;
  47.   THuffmanTree = array [0..510] of THuffmanNode;
  48.  
  49. type
  50.   THuffmanCodeStr = string[255];
  51.  
  52.   PHuffmanCode = ^THuffmanCode;
  53.   THuffmanCode = packed record
  54.     hcBitCount : longint;
  55.     hcCode     : array [0..31] of byte;
  56.   end;
  57.  
  58.   PHuffmanCodes = ^THuffmanCodes;
  59.   THuffmanCodes = array [0..255] of THuffmanCode;
  60.  
  61.  
  62. {===THuffmanPriorityQueue=============================================}
  63. type
  64.   longint = integer;
  65.  
  66.   THuffmanPriorityQueue = class
  67.     {-A priority queue for Huffman compression}
  68.     private
  69.       pqList : TList;
  70.       pqTree : PHuffmanTree;
  71.     protected
  72.       function pqGetCount : integer;
  73.  
  74.       procedure pqBubbleUp(aFromInx : integer; aItem : longint);
  75.       procedure pqTrickleDown(aFromInx : integer; aItem : longint);
  76.     public
  77.       constructor Create(aHTree : PHuffmanTree);
  78.         {-Create the priority queue}
  79.       destructor Destroy; override;
  80.         {-Dispose of the priority queue}
  81.  
  82.       procedure Add(aItem : longint);
  83.         {-Add an item (ie, Huffman tree index) to the priority queue}
  84.       function Remove : longint;
  85.         {-Remove and return the item (ie, Huffman tree index) with the
  86.           smallest count}
  87.  
  88.       property Count : integer read pqGetCount;
  89.         {-Count of items in the queue}
  90.  
  91.       property List : TList read pqList;
  92.   end;
  93. {--------}
  94. constructor THuffmanPriorityQueue.Create(aHTree : PHuffmanTree);
  95. begin
  96.   inherited Create;
  97.   {create the queue's array; we know it'll be at most 256 elements}
  98.   pqList := TList.Create;
  99.   pqList.Capacity := 256;
  100.   {remember the Huffman tree we're using}
  101.   pqTree := aHTree;
  102. end;
  103. {--------}
  104. destructor THuffmanPriorityQueue.Destroy;
  105. begin
  106.   pqList.Free;
  107.   inherited Destroy;
  108. end;
  109. {--------}
  110. procedure THuffmanPriorityQueue.Add(aItem : longint);
  111. begin
  112.   {add extra space at the end of the queue}
  113.   pqList.Count := pqList.Count + 1;
  114.   {now bubble the item up as far as it will go}
  115.   pqBubbleUp(pred(pqList.Count), aItem);
  116. end;
  117. {--------}
  118. procedure THuffmanPriorityQueue.pqBubbleUp(aFromInx : integer;
  119.                                            aItem    : longint);
  120. var
  121.   ParentInx : integer;
  122.   ItemCount : longint;
  123. begin
  124.   {while the item under consideration is smaller than its parent, swap
  125.    it with its parent and continue from its new position}
  126.   {Note: the parent for the child at index N is at (N-1) div 2}
  127.   ItemCount := pqTree^[aItem].hnCount;
  128.   ParentInx := (aFromInx - 1) div 2;
  129.   {while our item has a parent, and it's greater than the parent...}
  130.   while (aFromInx > 0) and
  131.         (ItemCount <
  132.            pqTree^[longint(pqList[ParentInx])].hnCount) do begin
  133.     {move our parent down the tree}
  134.     pqList[aFromInx] := pqList[ParentInx];
  135.     aFromInx := ParentInx;
  136.     ParentInx := (aFromInx - 1) div 2;
  137.   end;
  138.   {store our item in the correct place}
  139.   pqList[aFromInx] := pointer(aItem);
  140. end;
  141. {--------}
  142. function THuffmanPriorityQueue.pqGetCount : integer;
  143. begin
  144.   Result := pqList.Count;
  145. end;
  146. {--------}
  147. procedure THuffmanPriorityQueue.pqTrickleDown(aFromInx : integer;
  148.                                               aItem    : longint);
  149. var
  150.   ChildInx  : integer;
  151.   ListCount : integer;
  152.   ItemCount : longint;
  153. begin
  154.   {while the item under consideration is greater than one of its
  155.    children, swap it with the smaller child and continue from its new
  156.    position}
  157.   {Note: the children for the parent at index N are at (2N+1) and
  158.          2N+2}
  159.   ItemCount := pqTree^[aItem].hnCount;
  160.   ListCount := pqList.Count;
  161.   {calculate the left child index}
  162.   ChildInx := succ(aFromInx * 2);
  163.   {while there is at least a left child...}
  164.   while (ChildInx < ListCount) do begin
  165.     {if there is a right child, calculate the index of the smaller
  166.      child}
  167.     if (succ(ChildInx) < ListCount) and
  168.        (pqTree^[longint(pqList[ChildInx])].hnCount >
  169.           pqTree^[longint(pqList[succ(ChildInx)])].hnCount) then
  170.       inc(ChildInx);
  171.     {if our item is less or equal to the smaller child, we're done}
  172.     if (ItemCount <= pqTree^[longint(pqList[ChildInx])].hnCount) then
  173.       Break;
  174.     {otherwise move the smaller child up the tree, and move our item
  175.      down the tree and repeat}
  176.     pqList[aFromInx] := pqList[ChildInx];
  177.     aFromInx := ChildInx;
  178.     ChildInx := succ(aFromInx * 2);
  179.   end;
  180.   {store our item in the correct place}
  181.   pqList[aFromInx] := pointer(aItem);
  182. end;
  183. {--------}
  184. function THuffmanPriorityQueue.Remove : longint;
  185. begin
  186.   {return the item at the root}
  187.   Result := longint(pqList[0]);
  188.   {replace the root with the child at the lowest, rightmost position,
  189.    and shrink the list}
  190.   pqList[0] := pqList.Last;
  191.   pqList.Count := pqList.Count - 1;
  192.   {now trickle down the root item as far as it will go}
  193.   if (pqList.Count > 0) then
  194.     pqTrickleDown(0, longint(pqList[0]));
  195. end;
  196. {====================================================================}
  197.  
  198.  
  199. {===Exception handling===============================================}
  200. procedure RaiseWriteError;
  201. begin
  202.   raise Exception.Create('Cannot write to Huffman compressed stream');
  203. end;
  204. {--------}
  205. procedure RaiseReadError;
  206. begin
  207.   raise Exception.Create('Expecting more data in Huffman compressed stream, but none left');
  208. end;
  209. {--------}
  210. procedure RaiseReadCorruptError;
  211. begin
  212.   raise Exception.Create('Huffman compressed stream contains corrupted data');
  213. end;
  214. {====================================================================}
  215.  
  216.  
  217. {===Helper routines==================================================}
  218. procedure WriteBits(const aHCode    : THuffmanCode;
  219.                           aStream   : TStream;
  220.                       var aCollByte : byte;
  221.                       var aCollCount: integer);
  222. var
  223.   ByteNum : integer;
  224.   BitNum  : integer;
  225.   i       : integer;
  226.   TempCollByte  : byte;
  227.   TempCollCount : integer;
  228. begin
  229.   {make temporary copies of the var parameters for speed}
  230.   TempCollByte := aCollByte;
  231.   TempCollCount := aCollCount;
  232.   {start off with the correct mask}
  233.   ByteNum := 0;
  234.   BitNum := 7;
  235.   {for all bits...}
  236.   for i := 0 to pred(aHCode.hcBitCount) do begin
  237.     {shift collector byte left by one (sets low bit to zero)}
  238.     TempCollByte := TempCollByte shl 1;
  239.     {if the current bit is set, set low bit of the collector byte}
  240.     if (aHCode.hcCode[ByteNum] and Bit[BitNum]) <> 0 then
  241.       TempCollByte := TempCollByte or 1;
  242.     {we've added one more bit}
  243.     inc(TempCollCount);
  244.     {if the collector byte is full, write it out, reset bit count}
  245.     if (TempCollCount = 8) then begin
  246.       aStream.Write(TempCollByte, 1);
  247.       TempCollCount := 0;
  248.     end;
  249.     {get next bit}
  250.     if (BitNum = 0) then begin
  251.       BitNum := 7;
  252.       inc(ByteNum);
  253.     end
  254.     else
  255.       dec(BitNum);
  256.   end;
  257.   {set new values of var parameters}
  258.   aCollByte := TempCollByte;
  259.   aCollCount := TempCollCount;
  260. end;
  261. {--------}
  262. function ReadChar(aStream : TStream) : char;
  263. {-read a character from the stream}
  264. var
  265.   BytesRead : integer;
  266. begin
  267.   BytesRead := aStream.Read(Result, sizeof(char));
  268.   if (BytesRead <> sizeof(char)) then
  269.     RaiseReadError;
  270. end;
  271. {--------}
  272. function ReadValue(aStream : TStream) : longint;
  273. {-read an integer value from the stream}
  274. var
  275.   BytesRead : integer;
  276.   ValueType : byte;
  277. begin
  278.   Result := 0;
  279.   BytesRead := aStream.Read(ValueType, sizeof(ValueType));
  280.   if (BytesRead <> sizeof(ValueType)) then
  281.     RaiseReadError;
  282.   case ValueType of
  283.     vaByte :
  284.       begin
  285.         BytesRead := aStream.Read(Result, sizeof(byte));
  286.         if (BytesRead <> sizeof(byte)) then
  287.           RaiseReadError;
  288.       end;
  289.     vaWord :
  290.       begin
  291.         BytesRead := aStream.Read(Result, sizeof(word));
  292.         if (BytesRead <> sizeof(word)) then
  293.           RaiseReadError;
  294.       end;
  295.     vaLongint :
  296.       begin
  297.         BytesRead := aStream.Read(Result, sizeof(longint));
  298.         if (BytesRead <> sizeof(longint)) then
  299.           RaiseReadError;
  300.       end;
  301.   else {it's an unknown value type}
  302.     RaiseReadCorruptError;
  303.   end;{case}
  304. end;
  305. {--------}
  306. procedure WriteChar(aStream : TStream; aChar : char);
  307. {-write a character to the stream}
  308. var
  309.   BytesWrit : integer;
  310. begin
  311.   BytesWrit := aStream.Write(aChar, sizeof(char));
  312.   if (BytesWrit <> sizeof(char)) then
  313.     RaiseWriteError;
  314. end;
  315. {--------}
  316. procedure WriteValue(aStream : TStream; aValue : longint);
  317. {-write an integer value to the stream}
  318. var
  319.   BytesWrit : integer;
  320.   ValueType : byte;
  321. begin
  322.   {if the value is between 0 and 255 write a byte to the stream}
  323.   if (0 <= aValue) and (aValue < 256) then begin
  324.     ValueType := vaByte;
  325.     BytesWrit := aStream.Write(ValueType, sizeof(ValueType));
  326.     if (BytesWrit <> sizeof(ValueType)) then
  327.       RaiseWriteError;
  328.     BytesWrit := aStream.Write(aValue, sizeof(byte));
  329.     if (BytesWrit <> sizeof(byte)) then
  330.       RaiseWriteError;
  331.   end
  332.   {if the value is between 256 and 65535 write a word to the stream}
  333.   else if (256 <= aValue) and (aValue < 64*1024) then begin
  334.     ValueType := vaWord;
  335.     BytesWrit := aStream.Write(ValueType, sizeof(ValueType));
  336.     if (BytesWrit <> sizeof(ValueType)) then
  337.       RaiseWriteError;
  338.     BytesWrit := aStream.Write(aValue, sizeof(word));
  339.     if (BytesWrit <> sizeof(word)) then
  340.       RaiseWriteError;
  341.   end
  342.   {otherwise write a longint to the stream}
  343.   else begin
  344.     ValueType := vaLongint;
  345.     BytesWrit := aStream.Write(ValueType, sizeof(ValueType));
  346.     if (BytesWrit <> sizeof(ValueType)) then
  347.       RaiseWriteError;
  348.     BytesWrit := aStream.Write(aValue, sizeof(longint));
  349.     if (BytesWrit <> sizeof(longint)) then
  350.       RaiseWriteError;
  351.   end;
  352. end;
  353. {--------}
  354. procedure CalcCharDistribution(aStream : TStream;
  355.                                aHTree  : PHuffmanTree);
  356. {-calculate the character distribution from the data in the stream;
  357.   fill the first 256 entries in the Huffman tree with the information}
  358. var
  359.   i         : integer;
  360.   Buffer    : PByteArray;
  361.   BytesRead : integer;
  362. begin
  363.   aStream.Position := 0;
  364.   GetMem(Buffer, 1024);
  365.   try
  366.     BytesRead := aStream.Read(Buffer^, 1024);
  367.     while (BytesRead <> 0) do begin
  368.       for i := pred(BytesRead) downto 0 do
  369.         inc(aHTree^[Buffer^[i]].hnCount);
  370.       BytesRead := aStream.Read(Buffer^, 1024);
  371.     end;
  372.   finally
  373.     FreeMem(Buffer, 1024);
  374.   end;
  375. end;
  376. {--------}
  377. procedure ConvertCodeStr(const aHCode  : THuffmanCodeStr;
  378.                                aHCodes : PHuffmanCodes;
  379.                                aNodeInx: integer);
  380. {-convert a code string into binary; store in codes array}
  381. var
  382.   TempCode : THuffmanCode;
  383.   ByteNum  : integer;
  384.   BitNum   : byte;
  385.   i        : integer;
  386. begin
  387.   {set the binary code to zeros, so we only have to record '1' bits}
  388.   FillChar(TempCode, sizeof(TempCode), 0);
  389.   {store the code length}
  390.   TempCode.hcBitCount := length(aHCode);
  391.   {fill the bits from the left in the binary code}
  392.   ByteNum := 0;
  393.   BitNum := 7;
  394.   for i := 1 to length(aHCode) do begin
  395.     if (aHCode[i] = '1') then
  396.       TempCode.hcCode[ByteNum] :=
  397.          TempCode.hcCode[ByteNum] or Bit[BitNum];
  398.     if (BitNum = 0) then begin
  399.       BitNum := 7;
  400.       inc(ByteNum);
  401.     end
  402.     else
  403.       dec(BitNum);
  404.   end;
  405.   {store binary code in the codes array}
  406.   aHCodes^[aNodeInx] := TempCode;
  407. end;
  408. {--------}
  409. procedure CalcHuffmanCodePrim(aNodeInx : integer;
  410.                           var aHCode   : THuffmanCodeStr;
  411.                               aHTree   : PHuffmanTree;
  412.                               aHCodes  : PHuffmanCodes);
  413. {-recursive routine to calculate all the Huffman codes for a given
  414.   Huffman tree}
  415. begin
  416.   {if the current node is not a leaf, then visit the left subtree
  417.    followed by the right subtree}
  418.   if (aNodeInx >= 256) then begin
  419.     {add a 0 bit on the end of the code string}
  420.     inc(aHCode[0]);
  421.     aHCode[length(aHCode)] := '0';
  422.     {visit the left subtree}
  423.     CalcHuffmanCodePrim(aHTree^[aNodeInx].hnLeftInx, aHCode, aHTree, aHCodes);
  424.     {add a 1 bit on the end of the code string}
  425.     aHCode[length(aHCode)] := '1';
  426.     {visit the right subtree}
  427.     CalcHuffmanCodePrim(aHTree^[aNodeInx].hnRightInx, aHCode, aHTree, aHCodes);
  428.     dec(aHCode[0]);
  429.   end
  430.   {if the current node is a leaf, record the current code in the codes
  431.    array}
  432.   else begin
  433.     ConvertCodeStr(aHCode, aHCodes, aNodeInx);
  434.   end;
  435. end;
  436. {--------}
  437. procedure CalcHuffmanCodes(aHTree  : PHuffmanTree;
  438.                            aRoot   : integer;
  439.                            aHCodes : PHuffmanCodes);
  440. {-calculate the Huffman codes for a Huffman tree}
  441. var
  442.   HCode : THuffmanCodeStr;
  443. begin
  444.   {clear the codes array}
  445.   FillChar(aHCodes^, sizeof(aHCodes^), 0);
  446.   {to calculate the codes we have to visit every leaf and for each
  447.    leaf we'll have accumulated a series of bits (going left from a
  448.    parent node to a child node is a 0 bit, going right is a 1 bit);
  449.    for the walk through the tree we'll use a modified inorder
  450.    traversal (ie, visit the left subtree, there's no need to visit the
  451.    node itself, visit the right subtree); because we know the tree has
  452.    a maximum depth of 255, we'll use recursion without getting too
  453.    worried about blowing the stack}
  454.   HCode := '';
  455.   CalcHuffmanCodePrim(aRoot, HCode, aHTree, aHCodes);
  456. end;
  457. {--------}
  458. procedure ReadCharDistribution(aStream : TStream;
  459.                                aHTree  : PHuffmanTree);
  460. {-read a character distribution from a stream}
  461. var
  462.   i         : integer;
  463.   CharCount : integer;
  464.   Ch        : char;
  465. begin
  466.   {the first byte in the stream is the number of characters with non-
  467.    zero counts}
  468.   aStream.Position := 0;
  469.   CharCount := ord(ReadChar(aStream));
  470.   {read the characters and their counts}
  471.   for i := 0 to pred(CharCount) do begin
  472.     Ch := ReadChar(aStream);
  473.     aHTree^[ord(Ch)].hnCount := ReadValue(aStream);
  474.   end;
  475. end;
  476. {--------}
  477. procedure WriteCharDistribution(aStream : TStream;
  478.                                 aHTree  : PHuffmanTree);
  479. {-write a character distribution to a stream}
  480. var
  481.   i         : integer;
  482.   CharCount : byte;
  483. begin
  484.   {position the output stream}
  485.   aStream.Position := 0;
  486.   {the first byte in the stream is the number of characters with non-
  487.    zero counts; calculate this and output the answer}
  488.   CharCount := 0;
  489.   for i := 0 to 255 do
  490.     if (aHTree^[i].hnCount <> 0) then
  491.       inc(CharCount);
  492.   WriteChar(aStream, char(CharCount));
  493.   {now output those chars and their counts}
  494.   for i := 0 to 255 do
  495.     with aHTree^[i] do begin
  496.       if (hnCount <> 0) then begin
  497.         WriteChar(aStream, char(i));
  498.         WriteValue(aStream, hnCount);
  499.       end;
  500.     end;
  501. end;
  502. {--------}
  503. procedure BuildHuffmanTree(aHTree         : PHuffmanTree;
  504.                        var aLastParentInx : integer);
  505. {-given a Huffman tree just containing the character distributions,
  506.   build the entire tree; return the index of the root}
  507. var
  508.   i  : integer;
  509.   PQ : THuffmanPriorityQueue;
  510.   Node1Inx  : longint;
  511.   Node2Inx  : longint;
  512.   ParentInx : integer;
  513. begin
  514.   ParentInx := aLastParentInx;
  515.   {create a priority queue}
  516.   PQ := THuffmanPriorityQueue.Create(aHTree);
  517.   try
  518.     {add all the non-zero nodes to the queue}
  519.     for i := 0 to 255 do
  520.       if (aHTree^[i].hnCount <> 0) then
  521.         PQ.Add(i);
  522.     {SPECIAL CASE: there is only one non-zero node, ie the input
  523.      stream consisted of just one character, repeated one or more
  524.      times; set the parent index to the single character}
  525.     if (PQ.Count = 1) then
  526.       ParentInx := PQ.Remove
  527.     {otherwise we have the normal, many different chars, case}
  528.     else
  529.       {while there is more than one item in the queue, remove the two
  530.        smallest, join them to a new parent, and add the parent to the
  531.        queue}
  532.       while (PQ.Count > 1) do begin
  533.         Node1Inx := PQ.Remove;
  534.         Node2Inx := PQ.Remove;
  535.         inc(ParentInx);
  536.         with aHTree^[ParentInx] do begin
  537.           hnLeftInx := Node1Inx;
  538.           hnRightInx := Node2Inx;
  539.           hnCount := aHTree^[Node1Inx].hnCount +
  540.                      aHTree^[Node2Inx].hnCount;
  541.         end;
  542.         PQ.Add(ParentInx);
  543.       end;
  544.   finally
  545.     PQ.Free;
  546.   end;
  547.   aLastParentInx := ParentInx;
  548. end;
  549. {--------}
  550. procedure DoHuffmanCompression(aInStream  : TStream;
  551.                                aOutStream : TStream;
  552.                                aHCodes    : PHuffmanCodes);
  553. {-given an array of Huffman codes, compress the input stream to the
  554.   output stream}
  555. var
  556.   B : byte;
  557.   CollectorByte : byte;
  558.   BitCount      : integer;
  559.   i             : integer;
  560. begin
  561.   {reset the input stream to the start}
  562.   aInStream.Position := 0;
  563.   {for each character in the input stream, write its Huffman code to
  564.    the output stream}
  565.   CollectorByte := 0;
  566.   BitCount := 0;
  567.   for i := 0 to pred(aInStream.Size) do begin
  568.     aInStream.Read(B, sizeof(B));
  569.     WriteBits(aHCodes^[B], aOutStream, CollectorByte, BitCount);
  570.   end;
  571.   {if we've some bits left over write them out as well}
  572.   if (BitCount <> 0) then begin
  573.     {shift the bits to the top of the byte}
  574.     CollectorByte := CollectorByte shl (8 - BitCount);
  575.     aOutStream.Write(CollectorByte, 1);
  576.   end;
  577. end;
  578. {--------}
  579. procedure DoHuffmanDecompression(aInStream  : TStream;
  580.                                  aOutStream : TStream;
  581.                                  aHTree     : PHuffmanTree;
  582.                                  aRoot      : integer);
  583. {-given a Huffman tree, decompress the input stream to the output
  584.   stream}
  585. var
  586.   CharCount      : longint;
  587.   TotalCharCount : longint;
  588.   BitNum         : integer;
  589.   CollectorByte  : byte;
  590.   CurrNode       : integer;
  591.   GoLeft         : boolean;
  592.   Ch             : char;
  593. begin
  594.   {calculate the total number of characters to decompress; preset the
  595.    loop variables}
  596.   TotalCharCount := aHTree^[aRoot].hnCount;
  597.   CharCount := 0;
  598.   BitNum := 0;
  599.   CurrNode := aRoot;
  600.   {repeat until all the characters have been decompressed}
  601.   while CharCount < TotalCharCount do begin
  602.     {read the next bit}
  603.     if (BitNum = 0) then begin
  604.       aInStream.Read(CollectorByte, sizeof(CollectorByte));
  605.       BitNum := 7;
  606.     end
  607.     else
  608.       dec(BitNum);
  609.     GoLeft := (CollectorByte and Bit[BitNum]) = 0;
  610.     {walk down the Huffman tree}
  611.     if GoLeft then
  612.       CurrNode := aHTree^[CurrNode].hnLeftInx
  613.     else
  614.       CurrNode := aHTree^[CurrNode].hnRightInx;
  615.     {if we have reached a leaf, output the character concerned, and
  616.      reset the current node to the root}
  617.     if (CurrNode < 256) then begin
  618.       Ch := char(CurrNode);
  619.       aOutStream.Write(Ch, sizeof(byte));
  620.       CurrNode := aRoot;
  621.       inc(CharCount);
  622.     end;
  623.   end;
  624. end;
  625. {--------}
  626. procedure WriteMultipleChars(aStream : TStream;
  627.                              aCh     : char;
  628.                              aCount  : longint);
  629. {-write several copies of a character to a stream}
  630. const
  631.   BufferSize = 1024;
  632. var
  633.   Buffer       : PByteArray;
  634.   BytesToWrite : integer;
  635.   BytesWrit    : integer;
  636. begin
  637.   GetMem(Buffer, BufferSize);
  638.   try
  639.     FillChar(Buffer^, BufferSize, aCh);
  640.     while (aCount > 0) do begin
  641.       if (aCount < BufferSize) then
  642.         BytesToWrite := aCount
  643.       else
  644.         BytesToWrite := BufferSize;
  645.       BytesWrit := aStream.Write(Buffer^, BytesToWrite);
  646.       dec(aCount, BytesWrit);
  647.     end;
  648.   finally
  649.     FreeMem(Buffer, BufferSize);
  650.   end;
  651. end;
  652. {====================================================================}
  653.  
  654.  
  655. {===Interfaced routines==============================================}
  656. procedure HuffmanCompress(aInStream, aOutStream : TStream);
  657. var
  658.   HTree  : PHuffmanTree;
  659.   Root   : integer;
  660.   HCodes : PHuffmanCodes;
  661. begin
  662.   {if there's nothing to compress, exit now}
  663.   if (aInStream.Size = 0) then
  664.     Exit;
  665.   {allocate the Huffman tree}
  666.   New(HTree);
  667.   try
  668.     {initialise the tree}
  669.     FillChar(HTree^, sizeof(HTree^), 0);
  670.     {get the distribution of characters in the input stream, place in
  671.      the first 256 elements of the Huffman tree}
  672.     CalcCharDistribution(aInStream, HTree);
  673.     {build the Huffman tree}
  674.     Root := 255;
  675.     BuildHuffmanTree(HTree, Root);
  676.     {when this point is reached we know the Huffman tree is rooted at
  677.      Root; if Root is a leaf, then the input stream just consisted of
  678.      repetitions of one character, so output the minimal compressed
  679.      data, essentially RLE compression}
  680.     if (Root < 256) then
  681.       WriteCharDistribution(aOutStream, HTree)
  682.     else {Root is not a leaf} begin
  683.       {allocate the codes array}
  684.       New(HCodes);
  685.       try
  686.         {calculate all the codes}
  687.         CalcHuffmanCodes(HTree, Root, HCodes);
  688.         {we are now ready to compress the input stream, however we
  689.          must first output some information to the output stream to
  690.          aid the decompressor}
  691.         WriteCharDistribution(aOutStream, HTree);
  692.         {compress the characters in the input stream}
  693.         DoHuffmanCompression(aInStream, aOutStream, HCodes);
  694.       finally
  695.         Dispose(HCodes);
  696.       end;
  697.     end;
  698.   finally
  699.     Dispose(HTree);
  700.   end;
  701. end;
  702. {--------}
  703. procedure HuffmanDecompress(aInStream, aOutStream : TStream);
  704. var
  705.   HTree : PHuffmanTree;
  706.   Root  : integer;
  707. begin
  708.   {if there's nothing to decompress, exit now}
  709.   if (aInStream.Size = 0) then
  710.     Exit;
  711.   {allocate the Huffman tree}
  712.   New(HTree);
  713.   try
  714.     {initialise the tree}
  715.     FillChar(HTree^, sizeof(HTree^), 0);
  716.     {read the distribution of characters from the input stream, place
  717.      in the appropriate elements of the Huffman tree}
  718.     ReadCharDistribution(aInStream, HTree);
  719.     {build the Huffman tree}
  720.     Root := 255;
  721.     BuildHuffmanTree(HTree, Root);
  722.     {when this point is reached we know the Huffman tree is rooted at
  723.      Root; if Root is a leaf, then the original stream just consisted
  724.      of repetitions of one character}
  725.     if (Root < 256) then
  726.       WriteMultipleChars(aOutStream, char(Root), HTree^[Root].hnCount)
  727.     {otherwise, using the Huffman tree, decompress the characters in
  728.      the input stream; note that the number of chars to decompress
  729.      is the count at the root of the Huffman tree}
  730.     else
  731.       DoHuffmanDecompression(aInStream, aOutStream, HTree, Root);
  732.   finally
  733.     Dispose(HTree);
  734.   end;
  735. end;
  736. {====================================================================}
  737.  
  738. end.
  739.